﻿using System;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using MyOS.Models;
using MyOS.Views.ChildView;
using Panuon.UI.Silver;

namespace MyOS.Views
{
    public delegate void UpdateUIDelegate();

    /// <summary>
    ///     ProcessView.xaml 的交互逻辑
    /// </summary>
    public partial class ProcessView : UserControl
    {
        private const int TIME = 5;
        private const int TIME1 = 1;
        private const int SLEEP = 1000; //睡眠时间
        private CancellationTokenSource cts;
        private int DR; //
        private char[] IR; //指令寄存器
        private int PC; //程序计数器
        private PCBModel pcbNow; //正在运行的程序控制块
        private int pcbNumber = 1;
        private int PSW; //程序状态字
        private int RAM = 10; //内存计数器
        private Task task;
        private int Timer; //时间片

        public ProcessView()
        {
            InitializeComponent();
        }

        private void ShutdownButton_Click(object sender, RoutedEventArgs e)
        {
            cts.Cancel(); //点击取消时，将标志位设为cancel
        }

        private void BootButton_OnClick(object sender, RoutedEventArgs e) //开机
        {
            if (task != null) return; //使用task替代Thread，更安全
            cts = new CancellationTokenSource(); //在创建task时创建一个CancellationTokenSource，是一个类似令牌的结构

            task = new Task(CPU, cts.Token);
            task.Start();
        }

        private void CPU()
        {
            if (ReadyQueue.PcbStart == null)
            {
                //没有进程的时候就运行闲逛进程
                PSW = 4;
                Idle(); //闲逛进程
            }

            while (true)
            {
                try
                {
                    cts.Token.ThrowIfCancellationRequested(); //检测到标志位为cancel时抛出异常，就可以取消线程
                }
                catch (Exception ex)
                {
                    Dispatcher.Invoke(() => { Notice.Show("已关机。", "Info", 3, MessageBoxIcon.Info); });
                    task = null;
                    break;
                }

                if (PSW != 0)
                {
                    #region 时间片到，进程调度 PSW=1

                    switch (PSW)
                    {
                        case 1:
                            Dispatcher.Invoke(() => { TextBlockCommand.Text = "时间片到"; });
                            pcbNow.DR = DR;
                            pcbNow.PC = PC;
                            ReadyQueue.Add(pcbNow);
                            PSW = 4;
                            break;
                        case 2:
                        case 3:
                        {
                            Dispatcher.Invoke(() => { TextBlockCommand.Text = "唤醒进程"; });
                            Wake();
                            if (PSW != 4)
                                PSW -= 2;
                            break;
                        }
                        default:
                        {
                            if (PSW >= 4)
                            {
                                pcbNow = null;
                                //this.TextBlockCommand.Text = "进程调度";

                                Dispatcher.Invoke(() => { TextBlockCommand.Text = "进程调度"; });
                                if (ReadyQueue.PcbStart == null) Idle();

                                pcbNow = ReadyQueue.Get(); //中断当前进程后，从就绪队列得到一个进程开始执行
                                PC = pcbNow.PC; //取指令
                                if (pcbNow.Number == 0)
                                    Timer = TIME1;
                                else
                                    Timer = TIME; //分配时间片
                                DR = pcbNow.DR;
                                pcbNow.State = emState.operation; //状态改为执行
                                PSW -= 4;
                            }

                            break;
                        }
                    }

                    #endregion
                }
                else
                {
                    #region 取PC指令，放入IR寄存器

                    IR = new char[4];
                    IR[0] = pcbNow.Program[PC];
                    IR[1] = pcbNow.Program[PC + 1];
                    IR[2] = pcbNow.Program[PC + 2];
                    IR[3] = pcbNow.Program[PC + 3];

                    #endregion

                    #region pc++

                    PC += 4;

                    #endregion

                    #region 执行IR指令

                    if (Regex.IsMatch(new string(IR), @"x=\d;"))
                    {
                        DR = Convert.ToInt32(IR[2]) - 48;
                    }
                    else if (Regex.IsMatch(new string(IR), @"x\+\+;"))
                    {
                        DR++;
                    }
                    else if (Regex.IsMatch(new string(IR), @"x--;"))
                    {
                        DR--;
                    }
                    else if (Regex.IsMatch(new string(IR), @"![AB]\d;"))
                    {
                        pcbNow.Event = IR[1];
                        pcbNow.Timer = Convert.ToInt32(IR[2]) - 48;
                        pcbNow.DR = DR;
                        pcbNow.PC = PC;
                        BlockQueue.Add(pcbNow);
                        PSW = 4;
                    }
                    else if (Regex.IsMatch(new string(IR), @"end."))
                    {
                        RAM++;
                        TextBoxRam.Text = RAM.ToString();
                        var path = Path.ChangeExtension(pcbNow.Path, "out");
                        using (var stream = new FileStream(path, FileMode.Create))
                        {
                            var str = Path.GetFullPath(path) + "\r\n" + "x=" + DR;
                            var buffer = Encoding.UTF8.GetBytes(str);
                            stream.Write(buffer, 0, buffer.Length);
                        }

                        TextBlockLastResult.Text = DR.ToString();
                        PSW = 4;
                    }

                    #endregion

                    #region 时间片--，阻塞进程的时间--

                    Timer--;
                    if (Timer == 0 && PSW == 0) PSW = 1;
                    BlockTime();

                    #endregion

                    #region 页面更新

                    Dispatcher.Invoke(() => { TextBlockNum.Text = pcbNow.Number.ToString(); });
                    Dispatcher.Invoke(() => { TextBlockName.Text = pcbNow.Name; });
                    Dispatcher.Invoke(() => { TextBlockTimer.Text = pcbNow.Timer.ToString(); });
                    Dispatcher.Invoke(() => { TextBlockCommand.Text = new string(IR); });
                    Dispatcher.Invoke(() => { TextBlockResult.Text = DR.ToString(); });

                    #endregion
                }

                ReadyQueueReset();
                BlockQueueReset();
                //延时；
                Thread.Sleep(SLEEP);
            }
        }

        /**
       * 闲逛进程
       */
        private void Idle()
        {
            var path = @"idle.bin";
            var pcb = new PCBModel(path, 0);
            ReadyQueue.Add(pcb);
            ReadyQueueReset();
        }

        private void ReadyQueueReset()
        {
            lock (this)
            {
                Dispatcher.Invoke(() => { ListViewReady.Items.Clear(); });
                var pcb = ReadyQueue.PcbStart;
                while (pcb != null)
                {
                    Dispatcher.Invoke(() => { ListViewReady.Items.Add(new {pcb.Name, Num = pcb.Number.ToString()}); });
                    pcb = pcb.Next;
                }
            }
        }

        private void Wake()
        {
            var pcb = new PCBModel(@"idle.bin", 0);
            pcb.Next = BlockQueue.PcbStart;
            while (pcb.Next != null)
                if (pcb.Next.Timer == 0)
                {
                    if (pcb.Next == BlockQueue.PcbStart)
                    {
                        BlockQueue.PcbStart = BlockQueue.PcbStart.Next;
                        if (BlockQueue.PcbStart == null) BlockQueue.PcbEnd = null;
                    }

                    var temp = pcb.Next;
                    pcb.Next = temp.Next;
                    ReadyQueue.Add(temp);
                }
                else
                {
                    pcb = pcb.Next;
                }

            if (BlockQueue.PcbEnd != null) BlockQueue.PcbEnd = pcb;

            if (pcbNow.Number == 0) PSW = 4;
        }

        private void BlockTime()
        {
            var pcb = BlockQueue.PcbStart;
            while (pcb != null)
            {
                pcb.Timer--;
                if (pcb.Timer == 0)
                    //防止陷入死循环
                    if (PSW == 1 || PSW == 0 || PSW == 4)
                        PSW += 2;

                pcb = pcb.Next;
            }
        }

        private void BlockQueueReset()
        {
            lock (this)
            {
                Dispatcher.Invoke(() => { ListViewBlock.Items.Clear(); });
                ListViewItem item = null;
                var pcb = BlockQueue.PcbStart;
                while (pcb != null)
                {
                    Dispatcher.Invoke(() =>
                    {
                        ListViewBlock.Items.Add(new
                        {
                            pcb.Name, Num = pcb.Number, Event = pcb.Event.ToString(), Time = pcb.Timer.ToString()
                        });
                    });


                    pcb = pcb.Next;
                }
            }
        }

        private void ResultButton_OnClick(object sender, RoutedEventArgs e)
        {
            var window = new ResultView();
            if (Application.Current.MainWindow == null) return;
            ((MainWindow) Application.Current.MainWindow).IsMaskVisible = true;
            window.ShowDialog();
            ((MainWindow) Application.Current.MainWindow).IsMaskVisible = false;
        }

        private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (task != null)
            {
                if (RAM <= 0)
                {
                    MessageBox.Show("没有内存！");
                }
                else
                {
                    var comboBoxItem = ComboBox.SelectedItem as ComboBoxItem;
                    var text = comboBoxItem.Content.ToString();
                    text = text.Substring(text.Length - 1, 1);
                    var path = @"../data/" + text + ".in";
                    var pcb = new PCBModel(path, pcbNumber++);
                    ReadyQueue.Add(pcb);

                    if (pcbNow.Number == 0)
                        //当前进程是闲逛进程时，中断
                        PSW = 4;

                    RAM -= 1;
                    Dispatcher.Invoke(() => { TextBoxRam.Text = RAM.ToString(); });

                    ReadyQueueReset();
                }
            }
        }
    }
}